﻿#include <iostream>
#include <opencv2/opencv.hpp>
#include <Windows.h>
#define cmp(a, b) ((a > b) ? true : false)
#define CheckPos(Pos, row, col) ((((Pos & 0xFF) == col) && ((Pos & 0xFF00) == (row << 8))) ? true : false)
int32_t TrustNum = 5;
int32_t Difference = 2;
int32_t reTryNum, Base1, Base2, Base, rowNum, colNum;
using namespace std;
using namespace cv;
int32_t getAverage(int32_t *arr, int32_t num, int32_t TrustedNum)
{
	int32_t Sum = 0, Average = 0;
	double s = 0;
	for (int32_t i = 0; i < num; i++)
		Sum += arr[i];
	Average = Sum / num;
	for (int32_t i = 0; i < num; i++)
		s += (arr[i] - Average) * (arr[i] - Average);
	s /= (double)num;
	return (s < TrustedNum ? Average : 0);
}
int32_t getRow(Mat img, int32_t rowPos)
{
	bool isNewrow = 1;
	int32_t ret = 0;
	for (int32_t row = 0; row < rowPos; row += 2)
	{
		int32_t Sum = 0;
		for (int32_t col = 0; col < 600; col++)
			if (img.at<uchar>(row, col) < 200)
				Sum++;
		if (isNewrow && Sum >= 100)
		{
			isNewrow = 0;
			ret++;
		}
		else if (Sum < 100)
			isNewrow = 1;
	}
	return ret;
}
int32_t getCol(Mat img, int32_t colPos)
{
	bool isNewcol = 1;
	int32_t ret = 0;
	for (int32_t col = 0; col < colPos; col += 2)
	{
		int32_t Sum = 0;
		for (int32_t row = 0; row < 600; row++)
			if (img.at<uchar>(row, col) < 200)
				Sum++;
		if (isNewcol && Sum >= 100)
		{
			isNewcol = 0;
			ret++;
		}
		else if (Sum < 100)
			isNewcol = 1;
	}
	return ret;
}
int32_t getAverageOfBlock(Mat img, int32_t Block, int32_t GrayValue)
{
	if (Block)
	{
		int32_t right = 0, bottom = 0, Left = 0, High = 0, rowBlock = (Block / colNum) + ((Block % colNum) ? 1 : 0), colBlock = (Block % colNum) == 0 ? colNum : (Block % colNum),isNewrow = 0, isNewcol = 0;
		for (int32_t rowBlockTmp = 0; rowBlockTmp <= rowBlock && bottom <= 600; bottom++)
		{
			int32_t Sum = 0;
			for (int32_t col = 0; col < 600; col++)
				if (img.at<uchar>(bottom, col) <= GrayValue)
					Sum++;
			if (isNewrow && Sum >= 50)
			{
				rowBlockTmp += 1;
				if (rowBlockTmp == rowBlock)
					High = bottom;
				isNewrow = 0;
			}
			else if (Sum < 50)
			{
				if (rowBlockTmp == rowBlock)
					break;
				isNewrow = 1;
			}
		}
		for (int32_t colBlockTmp = 0; colBlockTmp <= colBlock && right <= 600; right++)
		{
			int32_t Sum = 0;
			for (int32_t row = 0; row < 600; row++)
				if (img.at<uchar>(row, right) <= GrayValue)
					Sum++;
			if (isNewcol && Sum >= 50)
			{
				if (colBlockTmp == colBlock - 1)
					Left = right;
				colBlockTmp += 1;
				isNewcol = 0;
			}
			else if (Sum < 50)
			{
				if (colBlockTmp == colBlock)
					break;
				isNewcol = 1;
			}
		}
		// cout << "BLOCK" << Block << endl;
		// cout << High << endl << bottom << endl << Left << endl << right << endl;
		if (!High || !bottom || !Left || !right)
			return -1;
		if (High == 600 || bottom == 600 || Left == 600 || right == 600)
			return 0;
		int32_t Sum = 0, times = 0, rowMax = High + ((bottom - High) / (rowNum > 30 ? 1 : 2));
		for (int32_t row = High; row < rowMax; row++)
		{
			int32_t tmp = 0;
			int32_t sumtmp = 0;
			int32_t *p = new int32_t[300]();
			for (int32_t col = Left; col < right; col++)
			{
				if (img.at<uchar>(row, col) < (reTryNum >= 200 ? 230 : 150))
				{
					p[tmp] = img.at<uchar>(row, col);
					sumtmp += p[tmp];
					tmp++;
				}
			}
			delete[](p);
			if (reTryNum < 200 && (tmp <= (colNum < 40 ? 5 : ((colNum < 50) ? 3 : (colNum < 70) ? 2 : 1)) || !getAverage(p, tmp, 256)))
				continue;
			Sum += sumtmp;
			times += tmp;
		}
		int32_t Average = times ? Sum / times : 0;
		return Average;
	}
	return 0;
}
int32_t saveToPos(Mat img, int32_t *Pos, int32_t row, int32_t col)
{
	if (Pos && row && col)
	{
		row = getRow(img, row);
		col = getCol(img, col);
		int32_t i = 0;
		while (Pos[i] != 0)
		{
			if (CheckPos(Pos[i], row, col))
			{
				Pos[i] += (1 << 16);
				return Pos[i];
			}
			i++;
		}
		Pos[i] = (row << 8) + col;
		return Pos[i];
	}
	else
		return 0;
}
int32_t getBlockNum(int32_t Pos)
{
	return ((Pos & 0xFF) + (((Pos & 0xFF00) >> 8) - 1) * colNum);
}
LONG MyUnhandledExceptionFilter(_EXCEPTION_POINTERS *EInfo)
{
	MessageBoxA(0, "你程序崩辣！", "乐", MB_TOPMOST);
	TerminateProcess((HANDLE)-1, 0);
	return EXCEPTION_EXECUTE_HANDLER;
}
int32_t main(int argc, char**argv)
{
	AddVectoredExceptionHandler(1, MyUnhandledExceptionFilter);
	Mat img, img2;
	if (argc < 2) {
		MessageBoxA(0, "请将图片拖到exe上以破解", 0, 0);
		TerminateProcess((HANDLE)-1, 0);
	}
	while (true) {
		reTryNum += 1;
		int32_t lineNum = 0, total = 0;
		img = imread(argv[1]);
		if (img.empty() || img.cols != 600 || img.rows != 600)
		{
			MessageBoxA(0, "读取图片出错", 0, 0);
			TerminateProcess((HANDLE)-1, 0);
		}
		img2.create(img.size(), 0);
		for (int32_t i = 0; i < 600; i++)
			for (int32_t j = 0; j < 600; j++)
				img2.at<uchar>(i, j) = saturate_cast<uchar>(0.114 * img.at<Vec3b>(i, j)[0] + 0.587 * img.at<Vec3b>(i, j)[1] + 0.299 * img.at<Vec3b>(i, j)[2]);
		int32_t *arr = new int32_t[360000]();
		int32_t *Pos = new int32_t[65536]();
		int32_t i = 0;
		for (int32_t row = 0; row < 600; row++)
		{
			for (int32_t col = 0; col < 600; col++)
			{
				arr[i] = img2.at<uchar>(row, col);
				i++;
			}
		}
		i = 0;
		rowNum = getRow(img2, 600);
		colNum = getCol(img2, 600);
		if (!rowNum || !colNum) {
			MessageBoxA(0, "色块识别失败", 0, 0);
			TerminateProcess((HANDLE)-1, 0);
		}
		if (reTryNum >= 200)
		{
			destroyAllWindows();
			cout << "常规破解程序已无法识别，本次处理采用色块平均值法，需要较长时间且无图片展示" << endl;
			int32_t DifferenceOfAverage = 0, Shown, DefaultGray = 180;
			int32_t test = 1;
			while (true) {
				for (; test <= rowNum * colNum; test++)
				{
					if (!getAverageOfBlock(img2, test, DefaultGray))
						break;
					if (getAverageOfBlock(img2, test, DefaultGray) == -1)
					{
						DefaultGray += 1;
						test -= 1;
					}
				}
				if (test < rowNum * colNum / 3 && DefaultGray < 200)
				{
					test = 1;
					DefaultGray += 2;
				}
				else break;
			}
			cout << "DefaultGray = " << DefaultGray << endl
				<< "行" << colNum << "宽" << rowNum << endl;
			Base = 0;
			int32_t TotalBlocks = 1;
			for (; TotalBlocks <= rowNum * colNum; TotalBlocks++)
			{
				if (!getAverageOfBlock(img2, TotalBlocks, DefaultGray))
					break;
				Base += getAverageOfBlock(img2, TotalBlocks, DefaultGray);
			}
			Base /= (TotalBlocks - 1);
			cout << "平均值" << Base << endl;
			do
			{
				DifferenceOfAverage++;
				Shown = 0;
				for (int32_t i = 3; i <= rowNum * colNum && Shown <= 10; i++)
					if (getAverageOfBlock(img2, i, DefaultGray) - DifferenceOfAverage >= Base)
						Shown++;
				if (Shown == 0)
					DifferenceOfAverage -= 1;
			} while (Shown > 10);
			cout << "区分度下限为：" << DifferenceOfAverage << endl;
			for (int32_t i = 1; i <= rowNum * colNum; i++)
				if (getAverageOfBlock(img2, i, DefaultGray) - DifferenceOfAverage >= Base)
					cout << "块" << i << "   与均色块差值:" << (getAverageOfBlock(img2, i, DefaultGray) - Base) << endl;
			cout << "破解完毕，回车退出...";
			getchar();
			TerminateProcess((HANDLE)-1, 0);
		}
		else
		{
			if (!Base1)
				Base1 = getAverageOfBlock(img2, 1, 200);
			if (!Base2)
				Base2 = getAverageOfBlock(img2, 2, 200);
			if (!Base)
			{
				if (abs(Base1 - Base2) < 7)
					Base = (Base1 + Base2) / 2;
				else
				{
					if (abs(getAverageOfBlock(img2, 3, 200) - Base1) > 7)
					{
						cout << "块1" << endl;
						total += 1;
						reTryNum = 1000;
						goto go;
					}
					else if (abs(getAverageOfBlock(img2, 3, 200) - Base2) > 7)
					{
						cout << "块2" << endl;
						total += 1;
						reTryNum = 1000;
						goto go;
					}
					else
					{
						delete[](arr);
						delete[](Pos);
						reTryNum = 1000;
						continue;
					}
				}
			}
			for (int32_t row = 0; row < 600; row++)
			{
				for (int32_t col = 0; col < 600; col++)
				{
					i++;
					if (arr[i] < 200 && col < 590)
					{
						if (!Base1 || !Base2 || !Base)
						{
							reTryNum = 1000;
							goto go;
						}
						int32_t tmp = 0;
						for (; arr[i + tmp] < 200; tmp++);
						if (tmp <= 5)
							continue;
						int32_t Average = getAverage(arr + i, tmp, TrustNum);
						if (!Average || Average - Base <= Difference)
							continue;
						lineNum++;
						line(img, Point(col, row), Point(col + tmp, row), Scalar(0, 0, 255), 4, LINE_AA);
						if ((saveToPos(img2, Pos, row, col + tmp / 2) >> 16) == 0xFFFF)
							goto go;
						i += tmp;
						col += tmp;
					}
				}
			}
		}
	go:
		delete[](arr);
		while (Pos[total] != 0)
			total++;
		if (total == 0)
		{
			delete[](Pos);
			TrustNum += 5;
			continue;
		}
		if (total > 10)
		{
			delete[](Pos);
			Difference += 2;
			continue;
		}
		cout << "行" << colNum << "宽" << rowNum << endl;
		for (int32_t count = 0; Pos[count] != 0; count++)
			cout << "块" << getBlockNum(Pos[count]) << "   置信度:" << (Pos[count] >> 16) << endl;
		imshow("破解结果", img);
		if (MessageBoxA(0, "是否有正确选项？", "确认", MB_YESNO) == IDNO)
		{
			delete[](Pos);
			system("cls");
			if (reTryNum >= 10)
				if (MessageBoxA(0, "是否使用地狱模式？", "确认", MB_YESNO) == IDYES)
					reTryNum = 1000;
			TrustNum += 5;
			continue;
		}
		else break;
	}
	return 0;
}